package com.ideaaedi.zoo.diy.feature.reqresp.encdec.impl.springmvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ideaaedi.zoo.commonbase.constant.AutoConfigurationConstant;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.api.executor.AesExecutor;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.api.properties.ZooReqrespEncdecProperties;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.api.serializer.ResponseEncryptSerializer;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.impl.springmvc.core.DefaultAesExecutor;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.impl.springmvc.core.RequestBodyDecryptFilter;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.impl.springmvc.core.ResponseBodyEncryptExecutor;
import com.ideaaedi.zoo.diy.feature.reqresp.encdec.impl.springmvc.properties.ZooReByBodyAdviceDIYGuideProperties;
import jakarta.servlet.Filter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StreamUtils;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

/**
 * 请求解密，响应加密 自动配置
 */
@EnableConfigurationProperties({ZooReByBodyAdviceDIYGuideProperties.class})
@ConditionalOnProperty(value = ZooReByBodyAdviceAutoConfiguration.ENABLE, havingValue = "true", matchIfMissing = true)
public class ZooReByBodyAdviceAutoConfiguration {
    
    public static final String ENABLE = "zoo.switch.feature-reqresp-encdec-by-springmvc.enable";
    
    @Bean
    @ConditionalOnMissingBean(name = RequestBodyDecryptFilter.BEAN_NAME)
    @ConditionalOnProperty(value = "zoo.req-resp-enc-dec.req-decrypt.enable", havingValue = "true")
    public FilterRegistrationBean<Filter> requestBodyDecryptFilter(ZooReqrespEncdecProperties encdecProperties, AesExecutor aesExecutor) {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        RequestBodyDecryptFilter filter = new RequestBodyDecryptFilter(encdecProperties, aesExecutor);
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(filter.getOrder());
        return registrationBean;
    }
    
    @Bean(name = ResponseBodyEncryptExecutor.BEAN_NAME)
    @ConditionalOnMissingBean(name = ResponseBodyEncryptExecutor.BEAN_NAME)
    @ConditionalOnProperty(value = "zoo.req-resp-enc-dec.resp-encrypt.enable", havingValue = "true")
    public ResponseBodyEncryptExecutor responseBodyEncryptExecutor(ZooReqrespEncdecProperties encdecProperties,
                                                                   AesExecutor aesExecutor,
                                                                   ObjectMapper objectMapper,
                                                                   ObjectProvider<ResponseEncryptSerializer> encryptSerializerProvider) {
        return new ResponseBodyEncryptExecutor(encdecProperties, aesExecutor, encryptSerializerProvider.getIfAvailable(), objectMapper);
    }
    
    /**
     * 解决返回的加密字符串多出引号的问题
     */
    @Bean
    @ConditionalOnProperty(value = "zoo.req-resp-enc-dec.resp-encrypt.enable", havingValue = "true")
    @ConditionalOnMissingBean(name = AutoConfigurationConstant.ZOO_MAPPING_JACKSON2_HTTP_MESSAGE_CONVERTER_BEAN_NAME)
    public MappingJackson2HttpMessageConverter zooMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        return new MappingJackson2HttpMessageConverter(objectMapper){
            @Override
            protected void writeInternal(@Nonnull Object object, Type type, @Nonnull HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
                if (object instanceof String) {
                    StreamUtils.copy((String) object, StandardCharsets.UTF_8, outputMessage.getBody());
                } else {
                    super.writeInternal(object, type, outputMessage);
                }
            }
        };
    }
    
    @Bean
    @ConditionalOnMissingBean(AesExecutor.class)
    public DefaultAesExecutor aesExecutor(ZooReqrespEncdecProperties encdecProperties) {
        return new DefaultAesExecutor(encdecProperties);
    }
    
}